package com.zrkizzy.module.content.service.article;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zrkizzy.common.core.utils.IdUtil;
import com.zrkizzy.common.core.utils.IpUtil;
import com.zrkizzy.common.core.utils.ServletUtil;
import com.zrkizzy.common.core.utils.StringUtil;
import com.zrkizzy.common.core.utils.bean.BeanCopyUtil;
import com.zrkizzy.common.models.domain.blog.article.Article;
import com.zrkizzy.common.models.dto.blog.article.ArticleDTO;
import com.zrkizzy.common.models.dto.blog.article.ArticleSettingsDTO;
import com.zrkizzy.common.models.dto.blog.article.command.ArticleCommandDTO;
import com.zrkizzy.common.models.dto.system.scheduler.SchedulerJobDTO;
import com.zrkizzy.common.models.enums.blog.article.ArticleStatus;
import com.zrkizzy.common.models.query.blog.article.ArticleQuery;
import com.zrkizzy.content.facade.service.article.IArticleService;
import com.zrkizzy.module.content.command.invoker.ArticleCommandInvoker;
import com.zrkizzy.module.content.constant.BlogArticleConst;
import com.zrkizzy.module.content.constant.DefaultValueConst;
import com.zrkizzy.module.content.enums.ArticleStatusEnum;
import com.zrkizzy.module.content.mapper.article.ArticleMapper;
import com.zrkizzy.scheduler.facade.service.ISchedulerJobService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 文章业务逻辑接口实现类
 * </p>
 *
 * @author zhangrongkang
 * @since 2023/9/18
 */
@Service
@Slf4j
public class ArticleServiceImpl implements IArticleService {

    @Autowired
    private IdUtil idUtil;

    @Autowired
    private ArticleCommandInvoker articleCommandInvoker;

    @Autowired
    private ISchedulerJobService schedulerJobService;

    @Autowired
    private ArticleMapper articleMapper;

    /**
     * 获取所有文章
     *
     * @param articleQuery 文章查询对象
     * @return 文章分页数据
     */
    @Override
    public Page<Article> listArticles(ArticleQuery articleQuery) {
        // 开启分页
        Page<Article> page = new Page<>(articleQuery.getCurrentPage(), articleQuery.getPageSize());
        // 查询并返回
        return articleMapper.listArticles(page, articleQuery);
    }

    /**
     * 添加或更新文章
     *
     * @param articleDTO 文章数据接收对象
     * @return 文章ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveArticle(ArticleDTO articleDTO) {
        return Objects.nonNull(articleDTO.getId()) ? updateArticle(articleDTO) : insertArticle(articleDTO);
    }

    /**
     * 获取指定文章信息
     *
     * @param articleId 文章ID
     * @return 文章数据返回对象
     */
    @Override
    public Article getArticleById(Long articleId) {
        return articleMapper.selectById(articleId);
    }
    
    /**
     * 批量删除文章数据
     *
     * @param ids 文章ID
     * @return true：删除成功，false：删除失败
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteBatch(List<Long> ids) {
        for (Long id : ids) {
            // 先删除文章关联数据
            articleMapper.deleteById(id);
            // 构建文章数据传输对象
            ArticleCommandDTO articleCommandDTO = ArticleCommandDTO.builder()
                    .articleId(id)
                    .build();
            articleCommandInvoker.executeDeleteCommands(articleCommandDTO);
        }
        return Boolean.TRUE;
    }

    /**
     * 更新文章设置属性
     *
     * @param articleSettingsDTO 文章设置属性数据传输对象
     * @return 是否更新成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateSettings(ArticleSettingsDTO articleSettingsDTO) {
        // 获取到文章ID
        Long articleId = articleSettingsDTO.getId();
        // 根据传来的ID获取到对应属性
        String field = ArticleStatus.getFieldByStatus(articleSettingsDTO.getStatus());
        // 健壮性判断字段值是否合法
        if (StringUtils.hasLength(field)) {
            // 根据当前字段名和列名获取对应字段值
            Boolean value = articleMapper.getFieldValue(articleId, field);
            // 返回更新结果
            return articleMapper.updateSettings(articleId, field, !value) == 1;
        }
        // 更新对应状态并返回结果
        return Boolean.FALSE;
    }

    /**
     * 更新文章发布状态
     *
     * @param articleId 文章ID
     * @return 是否更新成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateArticleStatus(Long articleId) {
        return articleMapper.updateArticleStatusById(articleId) == 1;
    }

    /**
     * 更新当前文章
     *
     * @param articleDTO 文章数据接收对象
     * @return 是否更新成功
     */
    private String updateArticle(ArticleDTO articleDTO) {
        // 对文章进行更新操作并返回响应结果
        if (articleMapper.updateById(BeanCopyUtil.copy(articleDTO, Article.class)) == 1) {
            // 文章命令数据查询对象
            ArticleCommandDTO articleCommandDTO = convertArticleCommand(articleDTO);
            articleCommandInvoker.executeUpdateCommands(articleCommandDTO);
        }
        // 更新文章的分类以及标签
        return String.valueOf(articleDTO.getId());
    }
    
    /**
     * 添加新的文章
     *
     * @param articleDTO 文章数据接收对象
     * @return 文章ID
     */
    private String insertArticle(ArticleDTO articleDTO) {
        // 设置文章属性值
        setArticleAttributeValue(articleDTO);
        // 如果文章数据添加成功则添加后续内容
        if (articleMapper.insert(BeanCopyUtil.copy(articleDTO, Article.class)) == 1) {
            // 转为文章命令查询对象
            ArticleCommandDTO articleCommandDTO = convertArticleCommand(articleDTO);
            // 调用添加文章命令，添加文章标签以及文章分类数据
            articleCommandInvoker.executeAddCommands(articleCommandDTO);
        }
        // 返回文章添加内容结果
        return String.valueOf(articleDTO.getId());
    }

    /**
     * 设置文章属性值
     *
     * @param articleDTO 文章数据传输对象
     */
    private void setArticleAttributeValue(ArticleDTO articleDTO) {
        // 生成文章ID
        articleDTO.setId(idUtil.nextId());
        // 设置默认访问量和浏览量
        articleDTO.setCommentCount(DefaultValueConst.DEFAULT_COMMENT_COUNT);
        articleDTO.setVisitCount(DefaultValueConst.DEFAULT_VISIT_COUNT);
        String ip = IpUtil.getIpAddress(ServletUtil.getRequest());
        // 设置文章的发布IP和IP属地
        articleDTO.setCreateAddress(ip);
        articleDTO.setCreateLocation(IpUtil.getIpLocation(ip));

        // 如果当前文章为待发布
        if (articleDTO.getStatus().equals(ArticleStatusEnum.WAIT_PUBLISH.getStatus())) {
            // 创建发布文章定时任务
            buildArticlePublishJob(articleDTO);
        }
    }

    /**
     * 构建定时发布文章定时任务
     *
     * @param articleDTO 文章数据传输对象
     */
    private void buildArticlePublishJob(ArticleDTO articleDTO) {
        // 构建基本定时任务数据传输对象
        SchedulerJobDTO schedulerJobDTO = schedulerJobService.buildNonConcurrentSchedulerJobDTO(articleDTO.getPublishTime());
        // 定时任务名称
        schedulerJobDTO.setJobName(StringUtil.messageFormat(BlogArticleConst.ARTICLE_JOB_NAME, articleDTO.getTitle()));
        // 定时任务分组
        schedulerJobDTO.setJobGroup(BlogArticleConst.ARTICLE_JOB_GROUP);
        // 调用目标
        schedulerJobDTO.setInvokeTarget(StringUtil.messageFormat(BlogArticleConst.ARTICLE_JOB_INVOKE_TARGET, articleDTO.getId()));
        // 任务备注
        schedulerJobDTO.setRemark(StringUtil.messageFormat(BlogArticleConst.ARTICLE_JOB_REMARK, schedulerJobDTO.getCreateBy(), articleDTO.getPublishTime(), articleDTO.getTitle()));
        log.info("发布文章定时任务对象: {}", schedulerJobDTO);

        // 创建文章发布定时任务
        schedulerJobService.saveSchedulerJob(schedulerJobDTO);
    }

    /**
     * 数据传输对象转为文章命令数据传输对象
     *
     * @param articleDTO 文章数据传输对象
     * @return 文章命令数据传输对象
     */
    private ArticleCommandDTO convertArticleCommand(ArticleDTO articleDTO) {
        return ArticleCommandDTO.builder().articleId(articleDTO.getId()).categoryId(articleDTO.getCategoryId()).tagIds(articleDTO.getTags()).build();
    }

}
